//
//  QueueTaskViewController.m
//  objective_c_language
//
//  Created by 王玉龙 on 2018/7/12.
//  Copyright © 2018年 longlong. All rights reserved.
//
#define CellIdentifer @"UITableViewCell"

#import "QueueTaskViewController.h"

@interface QueueTaskViewController ()
@property (nonatomic ,strong) NSArray<NSString *> *titleArray;
@end

@implementation QueueTaskViewController
- (void)viewDidLoad
{
    [super viewDidLoad];
    
    // Uncomment the following line to preserve selection between presentations.
    // self.clearsSelectionOnViewWillAppear = NO;
    
    // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
    // self.navigationItem.rightBarButtonItem = self.editButtonItem;
    
    [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:CellIdentifer];
    
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

#pragma mark - Table view data source

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.titleArray.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifer forIndexPath:indexPath];
    cell.textLabel.text = self.titleArray[indexPath.row];
    cell.textLabel.numberOfLines = 0;
    return cell;
}


- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
    
    
    if (indexPath.row == 0)
    {
        ///同步任务 + 并发队列
        [self syncTask_cocurrentQueue_Method];
    }
    else if (indexPath.row == 1)
    {
        ///同步任务 + 串行队列
        [self syncTask_serialQueue_Method];
    }
    else if (indexPath.row == 2)
    {
        ///同步任务 + 主队列（上下文是主队列）
        [self syncTask_mainQueue_Method];
    }
    else if (indexPath.row == 3)
    {
        ///同步任务 + 主队列（上下文是一个新建的串行队列）
        [self syncTask_mainQueue_Method2];
    }
    else if (indexPath.row == 4)
    {
        ///同步任务 + 主队列（上下文是一个新建的并发队列）
        [self syncTask_mainQueue_Method3];
    }
    else if (indexPath.row == 5)
    {
        ///异步任务 + 并发队列
        [self asyncTask_cocurrentQueue_Method];
    }
    else if (indexPath.row == 6)
    {
        //异步任务 + 串行队列
        [self asyncTask_serialQueue_Method];
    }
    else if (indexPath.row == 7)
    {
        ///异步任务 + 主队列
        [self asyncTask_mainQueue_Method];
    }
    else if (indexPath.row == 8)
    {
        
    }
}

///同步任务 + 并发队列
- (void)syncTask_cocurrentQueue_Method
{
    //在当前线程中执行任务，不会开启新线程，执行完一个任务，再执行下一个任务。
    NSLog(@"开始处理 === %@",NSThread.currentThread);
    
    //创建一个并发队列
    dispatch_queue_t queue = dispatch_queue_create("syncTask_cocurrentQueue_Method", DISPATCH_QUEUE_CONCURRENT);
    
    //将一个耗时任务A（同步）添加到 并发队列 queue 里处理
    dispatch_sync(queue, ^{
        NSLog(@"处理耗时任务A == %@",NSThread.currentThread);
        [NSThread sleepForTimeInterval:10];//模拟耗时任务
    });
    
    //追加一个耗时任务B（同步）添加到 并发队列 queue 里处理
    dispatch_sync(queue, ^{
        NSLog(@"处理耗时任务B == %@",NSThread.currentThread);
        [NSThread sleepForTimeInterval:10];//模拟耗时任务
    });
    
    NSLog(@"处理完毕 === %@",NSThread.currentThread);
    
    /*
     10:40:27.081068+0800  开始处理 === <NSThread: 0x6000000793c0>{number = 1, name = main}
     10:40:27.081331+0800  处理耗时任务A == <NSThread: 0x6000000793c0>{number = 1, name = main}
     10:40:37.083042+0800  处理耗时任务B == <NSThread: 0x6000000793c0>{number = 1, name = main}
     10:40:47.083970+0800  处理完毕 === <NSThread: 0x6000000793c0>{number = 1, name = main}
     */
}
/* 同步执行 + 并发队列中可看到：
 所有任务都是在当前线程（主线程）中执行的，没有开启新的线程（同步执行不具备开启新线程的能力）。
 所有任务都在打印的“开始处理 ===”和“处理完毕 ===”之间执行的（同步任务需要等待队列的任务执行结束）。
 任务按顺序执行的。
 按顺序执行的原因：虽然并发队列可以开启多个线程，并且同时执行多个任务。但是因为本身不能创建新线程，只有当前线程这一个线程（同步任务不具备开启新线程的能力），所以也就不存在并发。而且当前线程只有等待当前队列中正在执行的任务执行完毕之后，才能继续接着执行下面的操作（同步任务需要等待队列的任务执行结束）。所以任务只能一个接一个按顺序执行，不能同时被执行
 */


///同步任务 + 串行队列
- (void)syncTask_serialQueue_Method
{
    NSLog(@"开始处理 === %@",NSThread.currentThread);
    
    //创建一个串行队列
    dispatch_queue_t queue = dispatch_queue_create("syncTask_serialQueue_Method", DISPATCH_QUEUE_SERIAL);
    
    
    //将一个耗时任务A（同步）添加到 串行队列 queue 里处理
    dispatch_sync(queue, ^{
        NSLog(@"处理耗时任务A == %@",NSThread.currentThread);
        [NSThread sleepForTimeInterval:10];//模拟耗时任务
    });
    
    //追加一个耗时任务B（同步）添加到 串行队列 queue 里处理
    dispatch_sync(queue, ^{
        NSLog(@"处理耗时任务B == %@",NSThread.currentThread);
        [NSThread sleepForTimeInterval:10];//模拟耗时任务
    });
    
    NSLog(@"处理完毕 === %@",NSThread.currentThread);
    
    /*
     10:38:49.592478+0800  currentThread === <NSThread: 0x6000000793c0>{number = 1, name = main}
     10:38:49.592800+0800  处理耗时任务A == <NSThread: 0x6000000793c0>{number = 1, name = main}
     10:38:59.595534+0800  处理耗时任务B == <NSThread: 0x6000000793c0>{number = 1, name = main}
     10:39:09.596797+0800  处理完毕 === <NSThread: 0x6000000793c0>{number = 1, name = main}
     */
}
/*
 在同步执行 + 串行队列可以看到：
 所有任务都是在当前线程（主线程）中执行的，并没有开启新的线程（同步执行不具备开启新线程的能力）。
 所有任务都在打印的“开始处理 ===”和“处理完毕 ===”之间执行的（同步任务需要等待队列的任务执行结束）。
 任务是按顺序执行的（串行队列每次只有一个任务被执行，任务一个接一个按顺序执行）。
 */

///同步任务 + 主队列
- (void)syncTask_mainQueue_Method
{
    NSLog(@"开始处理 === %@",NSThread.currentThread);
    
    //获取主队列
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    
    //程序报错：Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
    //在主队列添加一个同步任务
    dispatch_sync(mainQueue, ^{
        NSLog(@"处理耗时任务A == %@",NSThread.currentThread);
        [NSThread sleepForTimeInterval:10];//模拟耗时任务
    });
    
    NSLog(@"处理完毕 === %@",NSThread.currentThread);
}
/* 同步任务 + 主队列
 开始执行dispatch_sync这个函数，主队列是串行队列，这个函数会把这个任务插入到主队列的最后面（理解队列添加任务）
 主线程执行到这里的时候就会等待插入的这个同步任务执行完之后再执行后面的操作
 但由于这个同步任务是插入到主队列的最后面，最队列前面的任务没有执行完之前是不会执行这个block的(主线程在执行initMainQueue任务)
 这样就造成了一个相互等待的过程，主线程在等待block完返回，block却在等待主线程执行它，这样就造成了死锁，看打印的信息你也就知道block是没有被执行的。
 */

- (void)syncTask_mainQueue_Method2
{
    //创建一个串行队列
    dispatch_queue_t queue = dispatch_queue_create("syncTask_mainQueue_Method2", DISPATCH_QUEUE_SERIAL);
    NSLog(@"开始处理 === %@",NSThread.currentThread);
    
    //获取主队列
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    
    //在新创建的串行队列列添加一个异步任务
    dispatch_sync(queue, ^{
        NSLog(@"currentThread === %@",NSThread.currentThread);
        //在主队列添加一个同步任务
        dispatch_sync(mainQueue, ^{
            
            //程序报错：Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
            NSLog(@"处理耗时任务A == %@",NSThread.currentThread);
            [NSThread sleepForTimeInterval:10];//模拟耗时任务
        });
        
        //追加一个耗时任务B（同步）添加到 串行队列 queue 里处理
        dispatch_sync(mainQueue, ^{
            NSLog(@"处理耗时任务B == %@",NSThread.currentThread);
            [NSThread sleepForTimeInterval:10];//模拟耗时任务
        });
    });
    
    NSLog(@"处理完毕 === %@",NSThread.currentThread);
}

- (void)syncTask_mainQueue_Method3
{
    //创建一个串行队列
    dispatch_queue_t queue = dispatch_queue_create("syncTask_mainQueue_Method3", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"开始处理 === %@",NSThread.currentThread);
    
    //获取主队列
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    
    //在新创建的串行队列列添加一个异步任务
    dispatch_async(queue, ^{
        NSLog(@"currentThread === %@",NSThread.currentThread);
        
        //在主队列添加一个同步任务
        dispatch_sync(mainQueue, ^{
            NSLog(@"处理耗时任务A == %@",NSThread.currentThread);
            [NSThread sleepForTimeInterval:10];//模拟耗时任务
        });
        
        //追加一个耗时任务B（同步）添加到 串行队列 queue 里处理
        dispatch_sync(mainQueue, ^{
            NSLog(@"处理耗时任务B == %@",NSThread.currentThread);
            [NSThread sleepForTimeInterval:10];//模拟耗时任务
        });
        
    });
    
    NSLog(@"处理完毕 === %@",NSThread.currentThread);
}
/*
 为什么现在就不会异常终止了呢？
 
 因为syncMain 任务放到了其他线程里，而任务A、任务B都在追加到主队列中，这2个任务都会在主线程中执行。
 syncMain 任务在其他线程中执行到追加任务1到主队列中，因为主队列现在没有正在执行的任务，所以，会直接执行主队列的任务1，等任务1执行完毕，再接着执行任务2、任务3。所以这里不会卡住线程。
 */



///异步任务 + 并发队列
- (void)asyncTask_cocurrentQueue_Method
{
    NSLog(@"开始处理 === %@",NSThread.currentThread);
    
    //创建一个并发队列
    dispatch_queue_t queue = dispatch_queue_create("syncTask_cocurrentQueue_Method", DISPATCH_QUEUE_CONCURRENT);
    
    //将一个耗时任务A（异步）添加到 并发队列 queue 里处理
    dispatch_async(queue, ^{
        NSLog(@"处理耗时任务A == %@",NSThread.currentThread);
        [NSThread sleepForTimeInterval:10];//模拟耗时任务
    });
    
    //追加一个耗时任务B（异步）添加到 并发队列 queue 里处理
    dispatch_async(queue, ^{
        NSLog(@"处理耗时任务B == %@",NSThread.currentThread);
        [NSThread sleepForTimeInterval:10];//模拟耗时任务
    });
    NSLog(@"处理完毕 === %@",NSThread.currentThread);
    
    /*
     10:49:10.827347+0800 开始处理 === <NSThread: 0x604000065c40>{number = 1, name = main}
     10:49:10.827608+0800 处理完毕 === <NSThread: 0x604000065c40>{number = 1, name = main}
     10:49:10.827708+0800 处理耗时任务B == <NSThread: 0x60000007e540>{number = 5, name = (null)}
     10:49:10.827710+0800 处理耗时任务A == <NSThread: 0x600000262500>{number = 4, name = (null)}
     */
}
/*
 在异步执行 + 并发队列中可以看出：
 
 除了当前线程（主线程），系统又开启了2个线程，并且任务是交替/同时执行的。（异步执行具备开启新线程的能力。且并发队列可开启多个线程，同时执行多个任务）。
 
 所有任务是在打印的“开始处理 ===”和“处理完毕 ===”之后才执行的。说明当前线程没有等待，而是直接开启了新线程，在新线程中执行任务（异步执行不做等待，可以继续执行任务）
 */

//异步任务 + 串行队列
- (void)asyncTask_serialQueue_Method
{
    NSLog(@"开始处理 === %@",NSThread.currentThread);
    
    //创建一个串行队列
    dispatch_queue_t queue = dispatch_queue_create("syncTask_cocurrentQueue_Method", DISPATCH_QUEUE_SERIAL);
    
    //将一个耗时任务A（异步）添加到串行队列 queue 里处理
    dispatch_async(queue, ^{
        NSLog(@"处理耗时任务A == %@",NSThread.currentThread);
        [NSThread sleepForTimeInterval:10];//模拟耗时任务
    });
    
    //追加一个耗时任务B（异步）添加到 串行队列 queue 里处理
    dispatch_async(queue, ^{
        NSLog(@"处理耗时任务B == %@",NSThread.currentThread);
        [NSThread sleepForTimeInterval:10];//模拟耗时任务
    });
    NSLog(@"处理完毕 === %@",NSThread.currentThread);
    
    /*
     10:50:40.141293+0800 开始处理 === <NSThread: 0x604000065c40>{number = 1, name = main}
     10:50:40.141591+0800 处理完毕 === <NSThread: 0x604000065c40>{number = 1, name = main}
     10:50:40.141641+0800 处理耗时任务A == <NSThread: 0x604000277140>{number = 6, name = (null)}
     10:50:50.147514+0800 处理耗时任务B == <NSThread: 0x604000277140>{number = 6, name = (null)}
     */
}
/*
 在异步执行 + 串行队列可以看到：
 开启了一条新线程（异步执行具备开启新线程的能力，串行队列只开启一个线程）。
 所有任务是在打印的“开始处理 ===”和“处理完毕 ===”之后才开始执行的（异步执行不会做任何等待，可以继续执行任务）。
 任务是按顺序执行的（串行队列每次只有一个任务被执行，任务一个接一个按顺序执行）。
 */

///异步任务 + 主队列
- (void)asyncTask_mainQueue_Method
{
    NSLog(@"开始处理 === %@",NSThread.currentThread);
    
    //获取主队列
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    
    //将一个耗时任务A（异步）添加到串行队列 queue 里处理
    dispatch_async(mainQueue, ^{
        NSLog(@"处理耗时任务A == %@",NSThread.currentThread);
        [NSThread sleepForTimeInterval:10];//模拟耗时任务
    });
    
    //追加一个耗时任务B（异步）添加到 串行队列 queue 里处理
    dispatch_async(mainQueue, ^{
        NSLog(@"处理耗时任务B == %@",NSThread.currentThread);
        [NSThread sleepForTimeInterval:10];//模拟耗时任务
    });
    NSLog(@"处理完毕 === %@",NSThread.currentThread);
    /*
     11:31:25.882905+0800  开始处理 === <NSThread: 0x60000007d400>{number = 1, name = main}
     11:31:25.883180+0800  处理完毕 === <NSThread: 0x60000007d400>{number = 1, name = main}
     11:31:25.885784+0800  处理耗时任务A == <NSThread: 0x60000007d400>{number = 1, name = main}
     11:31:35.886998+0800  处理耗时任务B == <NSThread: 0x60000007d400>{number = 1, name = main}
     */
}

/*
 异步执行 + 主队列可以看到：
 所有任务都是在当前线程（主线程）中执行的，并没有开启新的线程（虽然异步执行具备开启线程的能力，但因为是主队列，所以所有任务都在主线程中）。
 所有任务是在打印的“开始处理 ===”和“处理完毕 ===”之后才开始执行的（异步执行不会做任何等待，可以继续执行任务）。
 任务是按顺序执行的（因为主队列是串行队列，每次只有一个任务被执行，任务一个接一个按顺序执行）。
 */

#pragma mark - setter and getter

- (NSArray<NSString *> *)titleArray
{
    if (_titleArray == nil)
    {
        _titleArray = @[@"同步任务 + 并发队列",@"同步任务 + 串行队列",
                        @"同步任务 + 主队列（上下文在主队列）",@"同步任务 + 主队列（上下文在一个串行队列）",@"同步任务 + 主队列（上下文在一个并发队列）",@"异步任务 + 并发队列",@"异步任务 + 串行队列",@"异步任务 + 主队列"];
    }
    return _titleArray;
}

@end

